using System;
using System.Linq;
using System.Collections.Generic;
using FoundationLib.Mail.Exceptions;
using System.Text.RegularExpressions;
using System.Diagnostics.Contracts;


namespace FoundationLib.Mail 
{
	/// <summary>
	/// This class represents a parser for mail templates. The result of the parsing
	/// process is a set of variables found in the template. These variables are to be
	/// replaced by mail data items whose key matches the variable name.
	/// </summary>
	public class TemplateParser 
	{
		/// <summary>
		/// The mail template content to be parsed.
		/// </summary>
		private string content;
		/// <summary>
		/// The variable regular expression pattern
		/// </summary>
		private const string PATTERN = @"\{%([a-zA-Z]{1,}[a-zA-Z0-9]*)( style:(tr|li|p))?%\}";
		/// <summary>
		/// A set of variables found in the template
		/// </summary>
		private IList<TemplateVariable> variables;

		/// <summary>
		/// Creates a new template parser.
		/// </summary>
		public TemplateParser()
		{
			this.variables = new List<TemplateVariable>();
		}

		/// <summary>
		/// Creates a new template parser.
		/// </summary>
		/// <exception cref="ArgumentNullException">A null-reference was passed.
		/// </exception>
		/// <param name="content">The template content</param>
		public TemplateParser(string content) : this()
		{
			Contract.Requires(!String.IsNullOrWhiteSpace(content));
			this.content = content;
		}

		/// <summary>
		/// Gets or sets the mail template content to
		/// be parsed.
		/// </summary>
		public virtual string Content
		{
			get
			{
				return this.content;
			}
			set
			{
				Contract.Requires(!String.IsNullOrWhiteSpace(value));
				this.content = value;
			}
		}

		/// <summary>
		/// Creates the display style enumeration value from the given style string.
		/// </summary>
		/// <param name="style">The display style string</param>
		/// <returns>The display style enumeration value</returns>
		private static DisplayStyle CreateDisplayStyle(string style)
		{
			if (String.IsNullOrEmpty(style))
				return DisplayStyle.Text;

			switch (style)
			{
				case "p":
					return DisplayStyle.Text;
				case "tr":
					return DisplayStyle.TableRow;
				case "li":
					return DisplayStyle.ListItem;
				default:
					return DisplayStyle.Text;
			}
		}

		/// <summary>
		/// Parses the template and makes all parsed variables available to callers.
		/// </summary>
		/// <exception cref="ParsingException">A parsing error occurred.</exception>
		public virtual void Parse()
		{
			Contract.Assert(this.content != null);

			//Use regex pattern to find variables
			Regex regex = new Regex(PATTERN);
			Match match = regex.Match(this.content);
			while (match.Success)
			{
				TemplateVariable variable = null;

				if (match.Groups.Count == 2)
				{
					variable = new TemplateVariable(
						match.Groups[1].Value.Trim(),
						match.Groups[0].Value.Trim()
					);
				}
				else if (match.Groups.Count == 4)
				{
					DisplayStyle style = CreateDisplayStyle(match.Groups[3].Value);
					variable = new TemplateVariable(
						match.Groups[1].Value.Trim(),
						match.Groups[0].Value.Trim(),
						style
					);
				}

				if (variable != null)
					this.variables.Add(variable);
				match = match.NextMatch();
			}
		}

		/// <summary>
		/// Gets a set of template variables found in the template.
		/// </summary>
		public virtual TemplateVariable[] Variables
		{
			get
			{
				if (this.variables == null)
					return new TemplateVariable[] { };
				return (this.variables.ToArray());
			}
		}

	}

}